﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Win32;
using SharpPcap;
using WC3Game;

namespace DotaStats
{

    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class Window1 : Window
    {
        String networkFilter = String.Empty;

        PcapDevice captureDevice = null;
        List<PcapDevice> availableDevices = Pcap.GetAllDevices();

        int battleNetPort = 0;

        public Window1()
        {
            InitializeComponent();

            WC3Game.Action.HeroKilled += new WC3Game.Action.HeroDeathEventHandler(HeroDeathEvent);
            Game.GameStateChanged += new Game.GameStateChangeEventHandler(UpdateLog);

            battleNetPort = (int)Properties.Settings.Default["BattleNetPort"];

            PcapDevice selectedDevice =
                (from devices in availableDevices
                 where devices.Description == Properties.Settings.Default["CaptureDevice"].ToString()
                 select devices).SingleOrDefault();

            bool dupFilterPort = false;
            for (int port = 6112; port <= 6119; port++)
            {
                networkFilter += String.Format("tcp port {0} or ", port);

                if (port == battleNetPort)
                    dupFilterPort = true;
            }

            if (!dupFilterPort)
                networkFilter += String.Format("tcp port {0}", battleNetPort);
            else
                networkFilter = networkFilter.Substring(0, networkFilter.Length - 3);


            if (selectedDevice != null)
            {
                captureDevice = selectedDevice;

                if ((bool)Properties.Settings.Default["CaptureOnStart"])
                {
                    StartCapture();
                }
            }
        }

        void HeroDeathEvent(String victimName, String killerName, String gameTime, Kill.KillType killType)
        {
            String eventMsg = String.Empty;

            switch (killType)
            {
                case Kill.KillType.Hero:
                    eventMsg = String.Format("{0} killed {1} at {2}", killerName, victimName, gameTime);
                    break;
                case Kill.KillType.Tower:
                    eventMsg = "Unknown hero kill";
                    break;
                case Kill.KillType.Rax:
                    eventMsg = "Unknown hero kill";
                    break;
                case Kill.KillType.Courier:
                    eventMsg = String.Format("{0} killed {1}'s courier at {2}", killerName, victimName, gameTime);
                    break;
                default:
                    break;
            }

            UpdateLog(eventMsg);
        }

        void UpdateLog(string message)
        {
            logMessages += message;
            logMessages += System.Environment.NewLine;
        }

        private void MenuItem_Click(object sender, RoutedEventArgs e)
        {
            PreferencesWindow w = new PreferencesWindow();
            w.ShowDialog();

            if ((captureDevice == null && w.CaptureDeviceDescription != String.Empty) ||
                ((captureDevice != null && w.CaptureDeviceDescription != String.Empty) &&
                    captureDevice.Description != w.CaptureDeviceDescription))
            {
                // Prompt to start capturing
                MessageBoxResult result = MessageBox.Show(
                    "The capture interface has been changed. Would you like to restart capturing?",
                    "Interface Changed",
                    MessageBoxButton.YesNo
                    );

                if (result == MessageBoxResult.Yes)
                {
                    if (captureDevice != null)
                    {
                        // Close down existing capture device
                        captureDevice.StopCapture();
                        captureDevice.Close();
                    }

                    captureDevice =
                        (from device in availableDevices
                         where device.Description == w.CaptureDeviceDescription
                         select device).Single();

                    StartCapture();
                }
            }
        }

        private void StartCapture()
        {
            label1.Content = "Capturing Started...";

            captureDevice.Open();
            captureDevice.DumpOpen(String.Format("dotastats-{0}.pcap", DateTime.Now.ToString("yyyyMMddHHmmss")));

            captureDevice.OnPacketArrival += new Pcap.PacketArrivalEvent(captureDevice_OnPacketArrival);
            captureDevice.SetFilter(networkFilter);

            captureDevice.StartCapture();
        }

        public void StopCapture()
        {
            label1.Content = "Capturing Stopped...";
            captureDevice.StopCapture();            
            captureDevice.DumpClose();
            captureDevice.Close();
        }

        void captureDevice_OnPacketArrival(object sender, PcapCaptureEventArgs e)
        {
            if (captureDevice.DumpOpened)
                captureDevice.Dump(e.Packet);

            ProcessPacket(sender, e);
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            // Set up events/
            //Session.GameCreated += new Session.GameCreatedEventHandler(AddGame);
            Session.GameAdded += new Session.GameAddedEventHandler(GameAdded);
            //Session.PlayerAdded += new Session.PlayerEventHandler(AddPlayer);
            //Session.PlayerRemoved += new Session.PlayerEventHandler(RemovePlayer);

            // Start capturing.


        }

        private void GameAdded()
        {
            treeView1.Dispatcher.Invoke(new PlayerEventDelegate(UpdateTreeView), String.Empty);
        }

        private void AddGame(String name)
        {
            treeView1.Dispatcher.Invoke(new PlayerEventDelegate(AddNewGame), name);
        }

        System.Threading.Thread t = null;

        int gamePacketCount = 0;
        private void ImportPcapFile_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Multiselect = true;
            dialog.Filter = "Pcap File (*.pcap)|*.pcap";

            dialog.ShowDialog();
            if (dialog.FileName != String.Empty)
            {
                System.Threading.ThreadStart s = new System.Threading.ThreadStart(delegate { ProcessPcapFiles(dialog.FileNames); });
                System.Threading.Thread t = new System.Threading.Thread(s);
                t.Start();
            }
        }

        private void ProcessPcapFiles(String[] files)
        {
            foreach (String file in files)
            {
                PcapDevice device = Pcap.GetPcapOfflineDevice(file);
                device.OnPacketArrival += new Pcap.PacketArrivalEvent(ProcessPacket);

                device.Open();
                device.Capture(-1);
                device.Close();

                Console.WriteLine("End of file reached");
                Console.WriteLine("Processed {0} game packets.", gamePacketCount);
            }
        }

        UInt64 packetCount = 0;

        void ProcessPacket(object sender, PcapCaptureEventArgs e)
        {
            Session.ProcessPacket(e);
            packetCount++;

            if (packetCount % 50 == 0)
                label1.Dispatcher.Invoke(new UpdateStatusBarDelegate(UpdateStatusBar), packetCount);
        }

        void device_OnCaptureStopped(object sender, bool error)
        {

        }

        private delegate void UpdateTextBoxDelegate(String msg);
        private delegate void UpdateStatusBarDelegate(UInt64 packetCount);
        private delegate void PlayerEventDelegate(String name);

        String logMessages = String.Empty;


        private void UpdateStatusBar(UInt64 packetCount)
        {
            label1.Content = String.Format("{0} packets processed", packetCount);
        }

        private void UpdateTreeView(String blah)
        {
            treeView1.ItemsSource = Session.GameList;
        }

        private void AddNewGame(String name)
        {
            TreeViewItem headerItem = new TreeViewItem();
            headerItem.Header = name;

            treeView1.Items.Add(headerItem);
        }

        private void Window_Closed(object sender, EventArgs e)
        {
            if (t != null)
                t.Abort();

            StopCapture();
        }

        private void MenuItem_Click_1(object sender, RoutedEventArgs e)
        {
            Forms.ViewLogWindow v = new DotaStats.Forms.ViewLogWindow(logMessages);
            v.ShowDialog();
        }

        private void treeView1_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        {
            object selectedItem = treeView1.SelectedItem;
            Object navPage = null;

            Type selectedItemType = selectedItem.GetType();
            if (selectedItemType.IsAssignableFrom(typeof(Player)))
            {
                Player selectedPlayer = (Player)selectedItem;
                //navPage = new DotaStats.Pages.PlayerSummary(selectedPlayer);
                navPage = new DotaStats.Pages.PlayerPerformance(selectedPlayer.Parent, selectedPlayer, null);
            }

            if (selectedItemType.IsAssignableFrom(typeof(SubItem)))
            {
                SubItem selectedSubItem = (SubItem)selectedItem;

                if (selectedSubItem.Name == "Items")
                {
                    navPage = new DotaStats.Pages.PlayerItems(selectedSubItem.Parent);
                }

                if (selectedSubItem.Name == "Heroes")
                {
                }
            }

            if (selectedItemType.IsAssignableFrom(typeof(Game)))
            {
                Game selectedGame = (Game)selectedItem;
                navPage = new DotaStats.Pages.GameSummary(selectedGame);
            }

            if (selectedItemType.IsAssignableFrom(typeof(Team)))
            {
                Game g = ((Team)selectedItem).Game;
                navPage = new DotaStats.Pages.PlayerPerformance(g, null, (Team)selectedItem);
            }


            if (navPage != null)
                frame1.Navigate(navPage);
        }

        private void MenuItem_Click_2(object sender, RoutedEventArgs e)
        {
            frame1.Navigate(new DotaStats.Pages.About());
        }
    }

    public static class Extensions
    {
        public static string Reverse(this string str)
        {
            int len = str.Length;
            char[] arr = new char[len];

            for (int i = 0; i < len; i++)
            {
                arr[i] = str[len - 1 - i];
            }

            return new string(arr);
        }

    }
}
